library ieee;

use ieee.std_logic_1164.all;
--use ieee.numeric_std.all;

use work.cpu_utils.all;
use work.alu_utils.all;


entity cpu is
	generic (Tpd : Time := unit_delay);
-- reset, clk, magistrala
	port (
		reset : in bit;
		clock : in bit;
		d_bus : inout std_logic_vector (31 downto 0);
		a_bus : out std_logic_vector (31 downto 0);
		halt : out bit;
		rd, wr : out std_logic
	);


end cpu;


architecture implementation of cpu is
	
	component sp
		generic (Tpd : Time := unit_delay);
		port (
			clock : in bit;
			clear : in bit;
			inc : in bit;
			dec : in bit;	
			spV: out bit;
			q : out bit_vector(9 downto 0)
		);
	end component;
	
	component reg
		generic (
			size : integer := 32;
			Tpd : Time := unit_delay
			);
		port (
			clock : in bit;
			clear : in bit;
			load : in bit;
			d : in bit_vector(size-1 downto 0);
			q : out bit_vector(size-1 downto 0)
		);
	end component; 
	
	component alu
		generic (
			size: integer := 32;
			Tpd : Time := unit_delay
		);
		port (
			operand1 : in bit_vector(size-1 downto 0);
			operand2 : in bit_vector(size-1 downto 0);
			result : out bit_vector(size-1 downto 0);
			condition_flag : out bit_vector(2 downto 0);
			command : in ALU_command
		);
	end component;

	component controlUnit
		generic(
			register_size : integer := 32;
			Tpd : Time := unit_delay
		);
		port(
			reset_in: in bit;
			CC, V, SPV : in bit;
			opcode: in bit_vector (5 downto 0);--IR 5 downto 0
			
			halt_out: out bit;
			
			read: out bit;
			write: out bit;
			not_read: out bit;
			not_write: out bit;
			st_cond: out bit;
			
			ldIR: out bit;
			ldA: out bit;
			ldB: out bit;
			ldZ: out bit;
			ldMAR: out bit;
			ldMDR: out bit;
			
			mar_mux_s0: out bit;
			a_mux_s0: out bit;
			gpr_src_mux_s0: out bit;
			b_mux_s0: out bit;
			imm_mux_s0: out bit;
			imm_mux_s1: out bit;
			pc_gpr_mux_s0: out bit;
			mdr_mux_s0: out bit;
			mdr_mux_s1: out bit;
			
			alu_cmd: out ALU_command;
			
			Gpr_H_in: out bit;
			Gpr_L_in: out bit;
			ldIMM: out bit;
			
			push: out bit;
			pop: out bit;
			clSP: out bit;
			decSP: out bit;
			incSP: out bit;
			
			clPC: out bit;
			ldPC: out bit;
			
			clock: in bit
		);
	end component;
	
	component CC
		generic (
		Tpd: Time := unit_delay
		);
		port (
			N, Z: in bit;
			IR: in bit_vector (5 downto 0);
			st_cond: in bit;
			cond: out bit
		);
	end component;
	
	component reg_file
		generic (
			address_size : integer := 5; -- number of address bits
			register_size : integer := 32;
			Tpd : Time := unit_delay
			);
		port (
			clock : in bit;
			source_address_1 : in bit_vector(address_size-1 downto 0);
			data_1 : out bit_vector(register_size-1 downto 0);
			source_address_2 : in bit_vector(address_size-1 downto 0);
			data_2 : out bit_vector(register_size-1 downto 0);
			destination_address : in bit_vector(address_size-1 downto 0);
			data_3 : in bit_vector (register_size-1 downto 0);
			H_in, L_in : in bit);
	end component;
	
	component mux_2 
		generic ( 
			size: integer := 32;
			Tpd : Time := unit_delay);
		port (
				in_0, in_1 : in bit_vector(size-1 downto 0);
				y : out bit_vector(size-1 downto 0);
				sel : in bit);
	 end component;
	 
	 
	component mux_4 
		generic ( 
			size: integer := 32;
			Tpd : Time := unit_delay);
		port (
				in_0, in_1, in_2, in_3 : in bit_vector(size-1 downto 0);
				y : out bit_vector(size-1 downto 0);
				sel : in bit_vector(1 downto 0));
	 end component;

	 
	 
	 component stack		
		 generic (
				Tpd : Time := unit_delay;
				data_size: integer := 32;
				address_size : integer := 10 
			);
			port (
				clock : in bit;
				data_in : in bit_vector(data_size-1 downto 0);
				data_out : out bit_vector(data_size-1 downto 0);
				a_bus : in bit_vector(address_size-1 downto 0);
				push, pop: in bit
			);
	end component;
	
	
	
	
	
	signal IR_out : bit_vector(31 downto 0); -- delovi instrukcije za source-ve, destination i operaciju. Ovde treba da se vidi i za immed
	alias inst_reg_dest : bit_5 is IR_out(10 downto 6);
	alias inst_reg_s1 : bit_5 is IR_out(15 downto 11);
	alias inst_reg_s2 : bit_5 is IR_out(20 downto 16);
	alias inst_op : bit_6 is IR_out(5 downto 0);
	
	signal GPR_H_in : bit := '0';
	signal GPR_L_in : bit := '0';
	
	signal ALU_op : ALU_command;
	signal cond_flag: bit_vector(2 downto 0);
	
	--signal clock: bit := '0';
	signal clear_pc: bit := '0';
	signal load_pc: bit := '0';
	signal inc_pc: bit := '0';
	
	signal clear_sp: bit := '0';
	signal load_sp: bit := '0';
	signal inc_sp: bit := '0';
	signal dec_sp: bit := '0';
	
	signal clear_mbr: bit := '0';
	signal load_mbr: bit := '0';
	
	signal clear_mar: bit := '0';
	signal load_mar: bit := '0';
	
	signal clear_ir: bit := '0';
	signal load_ir: bit := '0';
	
	signal clear_A: bit := '0';
	signal load_A: bit := '0';
	
	signal clear_B: bit := '0';
	signal load_B: bit := '0';
	
	signal clear_Z: bit := '0';
	signal load_Z: bit := '0';
	
	signal clear_imm: bit := '0';
	signal load_imm: bit := '0';
	
	signal push_stack: bit := '0';
	signal pop_stack: bit := '0';
	
	signal MAR_mux_out: bit_32;
	signal MBR_mux_out: bit_32;
	signal A_mux_out: bit_32;
	signal B_mux_out: bit_32;
	--signal ALU_B_up_mux_out: bit_vector(26 downto 0);
	signal PC_GPR_mux_out: bit_32;
	signal GPR_mux_src_out: bit_5;
	signal IMM_mux_out: bit_32;
	
	signal MBR_out: bit_32;
	signal MAR_out: bit_32;
	signal PC_out: bit_32;
	signal ALU_out: bit_32;
	signal RS1_out: bit_32;
	signal RS2_out: bit_32;
	signal A_out: bit_32;
	signal B_out: bit_32;
	signal Z_out: bit_32;
	signal IMM_out: bit_32;
	
	signal SP_out: bit_vector(9 downto 0);
	signal stack_out: bit_32;
	
	signal PC_mux_sel: bit := '0';
	signal MAR_mux_sel: bit := '0';
	signal A_mux_sel: bit := '0';
	
	signal B_mux_sel: bit := '0';
	signal MBR_mux_sel: bit_vector (1 downto 0);
	signal IMM_mux_sel: bit_vector (1 downto 0);
	signal PC_GPR_mux_sel: bit := '0';
	signal GPR_mux_src_sel: bit := '0';
	signal spV: bit := '0';
	
	signal cond_cc: bit := '0';
	signal st_cond_sig: bit := '0';
	
	signal ctrl_read, ctrl_write : bit := '0';
	signal ctrl_not_read, ctrl_not_write : bit := '0';
	
  signal d_bus_bits: bit_32;
  signal imm_mux_in_0: bit_32;
  signal imm_mux_in_1: bit_32;
  signal imm_mux_in_2: bit_32;
  signal gpr_src_in_0: bit_5;
  signal gpr_src_in_1: bit_5;
  signal inst_src_2: bit_5;
begin	

  process(d_bus, clock)
  begin

	d_bus_bits <= to_bitvector(d_bus);
	end process;
	
	process(IR_out, clock)
	begin
	imm_mux_in_0<=(
							0 =>IR_out(31), 1 => IR_out(30),
							2 => IR_out(29), 3 => IR_out(28), 
							4 => IR_out(27), 5 => IR_out(26), 
							6 => IR_out(25), 7 => IR_out(24), 
							8 => IR_out(23), 9 => IR_out(22), 
							10 => IR_out(21), 11 => IR_out(20), 
							12 => IR_out(19), 13 => IR_out(18), 
							14 => IR_out(17), 15 => IR_out(16), 
							others=>IR_out(16)
							);
	gpr_src_in_0<=(
							0 =>IR_out(15), 1 => IR_out(14),
							2 => IR_out(13), 3 => IR_out(12), 
							4 => IR_out(11)
							);
	gpr_src_in_1<=(
							0 =>IR_out(10), 1 => IR_out(9),
							2 => IR_out(8), 3 => IR_out(7), 
							4 => IR_out(6)
							);
	inst_src_2<=(
							0 =>IR_out(20), 1 => IR_out(19),
							2 => IR_out(18), 3 => IR_out(17), 
							4 => IR_out(16)
							);
	imm_mux_in_2<=(
							0 => IR_out(31), 1 => IR_out(30), 
							2 => IR_out(29), 3 => IR_out(28), 
							4 => IR_out(27), 5 => IR_out(26), 
							6 => IR_out(25), 7 => IR_out(24), 
							8 => IR_out(23), 9 => IR_out(22), 
							10 => IR_out(21), 11 => IR_out(10), 
							12 => IR_out(9), 13 => IR_out(8), 
							14 => IR_out(7), 15 => IR_out(6), 
							others=>IR_out(6)
							);
	
	imm_mux_in_1<=(
							0 => IR_out(20), 1 => IR_out(19), 
							2 => IR_out(18), 3 => IR_out(17), 
							4 => IR_out(16),  others => '0'
							);
	end process;
	
	drive_abus: process(ctrl_read, ctrl_write, ctrl_not_read, ctrl_not_write, clock)
	begin
		if( ctrl_read='1' or ctrl_write='1' or ctrl_not_read='1' or ctrl_not_write='1' ) 
			then a_bus<=(to_stdlogicvector(MAR_out))after Tpd;
			else a_bus<=(a_bus'range=>'Z')after Tpd;
		end if;
	end process drive_abus;
	
	drive_dbus: process(ctrl_write, ctrl_not_write, clock)
	begin
		if( ctrl_write='1' or ctrl_not_write='1' ) 
			then d_bus<=(to_stdlogicvector(MBR_out))after Tpd;
			else d_bus<=(d_bus'range=>'Z')after Tpd;
		end if;
	end process drive_dbus;
	
	process (clock, ctrl_write, ctrl_not_write )
	begin
		if ctrl_not_write='1' 
			then wr<='0' after Tpd;
		elsif ctrl_write='1' 
			then wr<='1' after Tpd;
		else wr <= 'Z' after Tpd;
		end if;
	end process;
	
	process (clock, ctrl_read, ctrl_not_read)
	begin
		if ctrl_not_read='1' 
			then rd<='0' after Tpd;
		elsif ctrl_read='1' 
			then rd<='1' after Tpd;
		else rd <= 'Z' after Tpd;
		end if;
	end process;	
		
	--d_bus_bit_vect<=(to_bitvector(d_bus));
	
	regFile_imp: reg_file
		port map(
			clock => clock,
			source_address_1 => GPR_mux_src_out, data_1 => RS1_out, 
			source_address_2 => inst_src_2, data_2 => RS2_out, 
			destination_address => gpr_src_in_1, data_3 => PC_GPR_mux_out,
			H_in =>GPR_H_in, L_in => GPR_L_in);                                                   
					
	PC_imp: reg
		port map (
			clock => clock,
			clear => clear_pc, load => load_pc, 
			d => PC_GPR_mux_out, q => PC_out 
		);
	ALU_imp: alu
		port map(
					operand1 => A_mux_out, operand2 => B_mux_out, result => ALU_out, 
					condition_flag => cond_flag, command => ALU_op
				);
	SP_imp: sp 
			port map (
				clock => clock,
				clear => clear_sp, inc => inc_sp,
				dec => dec_sp, spV=>spV, q => SP_out
			);	
	ALU_A: reg
		port map (
				clock => clock,
				clear => clear_A, load => load_A,
				d => RS1_out, q => A_out
			);
	ALU_B: reg
		port map (
				clock => clock,
				clear => clear_B, load => load_B,
				d => RS2_out, q => B_out
			);
	ALU_Z: reg
		port map (
				clock => clock,
				clear => clear_Z, load => load_Z,
				d => ALU_out, q => Z_out
			);
	MBR: reg
		port map (
				clock => clock,
				clear => clear_mbr, load => load_mbr,
				d => MBR_mux_out, q => MBR_out
			);
	IR: reg 
		port map (
				clock => clock,
				clear => clear_ir, load => load_ir,
				d => MBR_out, q => IR_out
			);
	MAR: reg
		port map (
				clock => clock,
				clear => clear_mar, load => load_mar,
				d => MAR_mux_out, q => MAR_out
			);
	IMM: reg
		port map (
				clock => clock,
				clear => clear_imm, load => load_imm,
				d => IMM_mux_out, q => IMM_out
			);
			
	stack_imp: stack
		port map( clock => clock,
					data_in => MBR_out, data_out => stack_out, 
					a_bus => SP_out,	
					push => push_stack, pop=> pop_stack);

	MAR_mux: mux_2
		port map(
					in_0 => Z_out, in_1 => PC_out, y =>MAR_mux_out, sel => MAR_mux_sel);
	A_mux: mux_2
		port map(
					in_0 => PC_out, in_1 => A_out, y =>A_mux_out, sel => A_mux_sel);

	B_mux: mux_2
		port map(
					in_0 => B_out, in_1 => IMM_out, y =>B_mux_out, sel => B_mux_sel);
					
	PC_GPR_mux: mux_2
		port map(
					in_0 => Z_out, in_1 => MBR_out, y =>PC_GPR_mux_out, sel => PC_GPR_mux_sel);

				
	GPR_mux_src: mux_2 
		generic map( size=>5 )
		port map(
					in_0 => gpr_src_in_0, in_1 => gpr_src_in_1, y =>GPR_mux_src_out, sel => GPR_mux_src_sel);

	MBR_mux: mux_4 
		port map(
				in_0=> stack_out,  in_1 =>d_bus_bits, in_2 => Z_out, in_3 => PC_out, 
				y => MBR_mux_out,
				sel => MBR_mux_sel);
				
	IMM_mux: mux_4
		port map(
				in_0 => imm_mux_in_0,
				in_1 => imm_mux_in_1,
				in_2 => imm_mux_in_2,
				in_3 => (others => '0'), 
				y => IMM_mux_out,
				sel => IMM_mux_sel);
				
	CC_imp: CC
		port map (
			N => cond_flag(2), Z => cond_flag(1), 
			IR => inst_op, st_cond => st_cond_sig,
			cond => cond_CC
		);
		
	control_unit_imp: controlUnit
		port map(
		reset_in => reset,
		CC => cond_CC,  
		V=> cond_flag(0),
		SPV => spV, opcode => inst_op,  
		
		halt_out => halt,
		read => ctrl_read, write =>ctrl_write, 
		not_read => ctrl_not_read, not_write =>ctrl_not_write,
		st_cond => st_cond_sig,
		
		ldIR => load_ir, ldA => load_A, ldB => load_B, ldZ => load_Z, ldMAR => load_MAR, ldMDR => load_MBR, 
		mar_mux_s0=> MAR_mux_sel, a_mux_s0 => A_mux_sel, gpr_src_mux_s0 => GPR_mux_src_sel, 
		b_mux_s0 => B_mux_sel, imm_mux_s0 => IMM_mux_sel(0), imm_mux_s1 => IMM_mux_sel(1),
		pc_gpr_mux_s0 => PC_GPR_mux_sel, mdr_mux_s0 => MBR_mux_sel(0), mdr_mux_s1 => MBR_mux_sel(1), 
		
		alu_cmd => ALU_op, 
		
		Gpr_H_in => GPR_H_in, Gpr_L_in => GPR_L_in, 
		ldIMM => load_imm, 	
		push => push_stack, pop => pop_stack, clSP => clear_sp, decSP => dec_sp, incSP => inc_sp, 
		
		clPC => clear_pc, ldPC => load_pc, 
		clock => clock
	);
	
	
end implementation;

configuration impl_config of cpu is
	for implementation
			for all: reg_file
				use entity work.reg_file(reg_file_arh);
			end for;
		    for all: mux_2
				use entity work.mux_2(mux_2_arh);
			end for;
			for all: mux_4
				use entity work.mux_4(mux_4_arh);
			end for;
			for all: reg
				use entity work.reg(reg_arh);
			end for;
			for all: alu
				use entity work.alu(alu_arh);
			end for;
			for all: sp
				use entity work.sp(sp_arh);
			end for;
			for all: stack
				use entity work.stack(stack_mem);
			end for;
			for all: controlUnit
				use entity work.controlUnit(behavioral);
			end for;
			for all: CC
				use entity work.CC(behavioral);
			end for;
	end for;
end configuration impl_config;




						
				